/*
 * Copyright 2011 Mark Davidson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.dctmcommons;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dctmcommons.impl.QueryHelper;
import org.junit.*;

import com.documentum.fc.client.IDfCollection;
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfTypedObject;
import com.documentum.fc.common.DfDocbaseConstants;
import com.documentum.fc.common.DfException;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

/**
 * Integration tests for {@link QueryHelper}.
 */
public class QueryHelperIT {

	/** Serial id. */
	private static final long serialVersionUID = -8960486961176268549L;
	
	/** DQL for getting system cabinet details. */
	private static final String DQL_GET_SYSTEM_CABINET_DETAILS =
		"select r_object_id, object_name from dm_cabinet " +
		"where object_name = 'System'";
	
	/** DQL for getting invalid cabinet details. */
	private static final String DQL_GET_INVALID_CABINET_DETAILS =
		"select r_object_id, object_name from dm_cabinet " +
		"where object_name = 'InvalidCabinet'";
	
	/** DQL for getting system cabinet name. */
	private static final String DQL_GET_SYSTEM_CABINET_NAME =
		"select object_name from dm_cabinet " +
		"where object_name = 'System'";
	
	/** DQL for getting invalid cabinet name. */
	private static final String DQL_GET_INVALID_CABINET_NAME =
		"select object_name from dm_cabinet " +
		"where object_name = 'InvalidCabinet'";
	
	/** DQL for getting version label for menusystem.ini (known file). */
	private static final String DQL_GET_MENUSYSTEM_INI_VERSION_LABEL =
		"select r_version_label, r_object_id from dm_menu_system";
	
	/** The query helper instance. */
	private IQueryHelper queryHelper = null;
	
	/**
	 * Tests the factory method.
	 */
	@Test
	public void testGetInstance() {
		System.out.println(">>> testGetInstance");
		assertThat("Factory method returned null",
				   QueryHelper.getInstance(),
				   is(notNullValue()));
	}

	/**
	 * Tests the runMethod method.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testRunMethod() throws DfException {
		System.out.println(">>> testRunMethod");
		
		final String methodName = "dm_QueueMgt";
		final Map<String, String> methodArgs = new HashMap<String, String>();
		methodArgs.put("window_interval", "120");
		methodArgs.put("queueperson", null);
		methodArgs.put("cutoff_days", "90");
		methodArgs.put("custom_predicate", "");
		
		try {
			queryHelper.runMethod(DctmCommonsTestUtil.getSession(),
								  methodName,
								  methodArgs);
		} catch (DfException e) {
			fail("Failed to run method: " + methodName);
		}
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests the query method.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQuery() throws DfException {
		System.out.println(">>> testQuery");
		
		IDfCollection results = null;
		
		try {
			results = queryHelper.query(DctmCommonsTestUtil.getSession(),
										DQL_GET_SYSTEM_CABINET_NAME,
										IDfQuery.DF_READ_QUERY);
			assertThat("Results collection is null",
					   results,
					   is(notNullValue()));
			
			int numResults = 0;
			while (results.next()) {
				numResults++;
				final String cabinetName = results.getString(
												DfDocbaseConstants.OBJECT_NAME);
				assertThat("Incorrect system cabinet name: " + cabinetName,
						   cabinetName,
						   is("System"));
			}
			
			assertThat("Incorrect number of matching system cabinets found",
					   numResults,
					   is(1));
		} finally {
			queryHelper.closeCollection(results);
			assertThat("Collection is not closed: " + results.getState(),
					   results.getState(),
					   is(IDfCollection.DF_CLOSED_STATE));
			DctmCommonsTestUtil.releaseSession();
		}
	}
	
	/**
	 * Tests the queryForCount method.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForCount() throws DfException {
		System.out.println(">>> testQueryForCount");
		
		assertThat("Count should be zero for non-existant object_name",
				   queryHelper.queryForCount(
					DctmCommonsTestUtil.getSession(),
					"dm_xml_application where object_name = 'frrpwibbleparp'"),
				   is(0));
		
		assertThat("Count should be 1 for known dm_menu_system object",
				   queryHelper.queryForCount(
						   DctmCommonsTestUtil.getSession(),
					   "dm_menu_system where object_name = 'MenuSystem.ini'"),
				   is(1));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests querying for a single string value.
	 * @throws DfException The repository exception
	 * results
	 */
	@Test
	public void testQueryForStringValue() throws DfException {
		System.out.println(">>> testQueryForStringValue");
		
		String attribValue = null;
		
		attribValue = queryHelper.queryForSingleStringValue(
											DctmCommonsTestUtil.getSession(),
											DQL_GET_SYSTEM_CABINET_NAME,
											IDfQuery.DF_READ_QUERY);
		assertThat("Attrib value is null", attribValue, is(notNullValue()));
		assertThat("Incorrect cabinet name", attribValue, is("System"));
		
		attribValue = queryHelper.queryForSingleStringValue(
											DctmCommonsTestUtil.getSession(),
											DQL_GET_INVALID_CABINET_NAME,
											IDfQuery.DF_READ_QUERY);
		assertThat("Attrib value is not null", attribValue, is(nullValue()));
		
		DctmCommonsTestUtil.releaseSession();

	}
	
	/**
	 * Tests querying for a repeating string value.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForRepeatingStringValue()
		throws DfException {
		System.out.println(">>> testQueryForRepeatingStringValue");
		
		String[] attribValues = null;
		
		attribValues = queryHelper.queryForRepeatingStringValue(
										DctmCommonsTestUtil.getSession(),
										DQL_GET_MENUSYSTEM_INI_VERSION_LABEL,
										IDfQuery.DF_READ_QUERY);
		
		assertThat("Attrib values array is null",
				   attribValues,
				   is(notNullValue()));

		// assumption is that the menu system object r_version_label
		// has 2 values - '<version_no>' and 'CURRENT'
		assertThat("Attrib values array size is incorrect",
				   attribValues.length,
				   is(2));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests querying for a list of map objects.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForListOfMaps()
		throws DfException {
		System.out.println(">>> testQueryForListofMaps");
		
		List<Map<String, Object>> cabinets = null;
		Map<String, Object> cabinet = null;
		
		cabinets = queryHelper.queryForListOfMaps(
											DctmCommonsTestUtil.getSession(),
											DQL_GET_SYSTEM_CABINET_DETAILS,
											IDfQuery.DF_READ_QUERY);
		
		assertThat("Null results", cabinets, is(notNullValue()));
		assertThat("Incorrect number of hits", cabinets.size(), is(1));
		
		cabinet = cabinets.get(0);
		assertThat("Null cabinet", cabinet, is(notNullValue()));
		assertThat("Incorrect cabinet name",
				   (String) cabinet.get("object_name"),
				   is("System"));
		
		cabinets = queryHelper.queryForListOfMaps(
											DctmCommonsTestUtil.getSession(),
										    DQL_GET_INVALID_CABINET_DETAILS,
										    IDfQuery.DF_READ_QUERY);

		assertThat("Results should be null", cabinets, is(nullValue()));	
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests execution of query for valid cabiet using callback mechanism.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForValidCabinet()
		throws DfException {
		System.out.println(">>> testQueryForValidCabinet");
		
		final int[] rows = new int[1];
		
		queryHelper.query(DctmCommonsTestUtil.getSession(),
						  DQL_GET_SYSTEM_CABINET_DETAILS,
						  IDfQuery.DF_EXEC_QUERY,
						  new ITypedObjectCallback() {
			
			@Override
			public boolean read(final IDfTypedObject results)
				throws DfException {
				try {
					assertThat("Null object id",
							   results.getString("r_object_id"),
							   is(notNullValue()));
					assertThat("Incorrect cabinet name",
							   results.getString("object_name"),
							   is("System"));
					rows[0]++;
				} catch (DfException e) {
					fail("Failed to read system cabinet query results");
					throw new DctmCommonsRuntimeException(e);
				}
				return true;
			}
		});
		
		assertThat("Invalid row count (" + rows[0] + ")",
				   rows[0],
				   is(equalTo(1)));
		
		DctmCommonsTestUtil.releaseSession();
	}

	/**
	 * Tests execution of query for invalid cabinet using callback mechanism.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForInvalidCabinet()
		throws DfException {
		System.out.println(">>> testQueryForInvalidCabinet");
		
		final int[] rows = new int[1];
		
		queryHelper.query(DctmCommonsTestUtil.getSession(),
						  DQL_GET_INVALID_CABINET_DETAILS,
						  IDfQuery.DF_EXEC_QUERY,
						  new ITypedObjectCallback() {
			
			@Override
			public boolean read(final IDfTypedObject results)
				throws DfException {
				rows[0]++;
				return true;
			}
		});
		
		assertThat("Invalid row count (" + rows[0] + ")",
				   rows[0],
				   is(equalTo(0)));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests execution of query for single int value.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForIntValue()
		throws DfException {
		System.out.println(">>> testQueryForIntValue");
		
		final int value = queryHelper.queryForSingleIntValue(
										DctmCommonsTestUtil.getSession(),
										"select r_page_cnt from dm_menu_system",
										IDfQuery.DF_READ_QUERY);
		assertThat("Invalid page count value", value, is(1));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests execution of query for single date value.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForDateValue()
		throws DfException {
		System.out.println(">>> testQueryForDateValue");
		
		final Date value = queryHelper.queryForSingleDateValue(
											DctmCommonsTestUtil.getSession(),
											"select r_modify_date " +
											"from dm_menu_system",
											IDfQuery.DF_READ_QUERY);
		assertThat("Null modification date", value, is(notNullValue()));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Tests execution of query for single boolean value.
	 * @throws DfException The repository exception
	 */
	@Test
	public void testQueryForBooleanValue()
		throws DfException {
		System.out.println(">>> testQueryForBooleanValue");
		
		final boolean value = queryHelper.queryForSingleBooleanValue(
											DctmCommonsTestUtil.getSession(),
											"select i_is_deleted " +
											"from dm_menu_system",
											IDfQuery.DF_READ_QUERY);
		assertThat("Incorrect deleted flag", value, is(false));
		
		DctmCommonsTestUtil.releaseSession();
	}
	
	/**
	 * Setup routine.
	 */
	@Before
	public void before() throws DfException {
		queryHelper = QueryHelper.getInstance();
	}
	
	/**
	 * Teardown routine.
	 */
	@After
	public void after() throws DfException {
	}
}
